﻿# ====================================================================================
# EXTERNAL FUNCTIONS
# ====================================================================================

#Region DisableIEEnhancedSecurity
Function DisableIEEnhancedSecurity([xml]$xmlinput)
{
    WriteLine
    If ($xmlinput.Configuration.Settings.DisableIEEnhancedSecurity -eq "True")
    {
        Write-Host -ForegroundColor White " - Disabling IE Enhanced Security..."
        Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" -Name isinstalled -Value 0
        Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" -Name isinstalled -Value 0
        Rundll32 iesetup.dll, IEHardenLMSettings,1,True
        Rundll32 iesetup.dll, IEHardenUser,1,True
        Rundll32 iesetup.dll, IEHardenAdmin,1,True
        If (Test-Path "HKCU:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}")
        {
            Remove-Item -Path "HKCU:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}"
        }
        If (Test-Path "HKCU:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}")
        {
            Remove-Item -Path "HKCU:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}"
        }

        #This doesn't always exist
        Remove-ItemProperty "HKCU:\SOFTWARE\Microsoft\Internet Explorer\Main" "First Home Page" -ErrorAction SilentlyContinue
    }
    Else
    {
        Write-Host -ForegroundColor White " - Not configured to change IE Enhanced Security."
    }
    WriteLine
}
#EndRegion

#Region Disable Loopback Check
# ===================================================================================
# Func: DisableLoopbackCheck
# Desc: Disable Loopback Check
# ===================================================================================
Function DisableLoopbackCheck([xml]$xmlinput)
{
    # Disable the Loopback Check on stand alone demo servers.
    # This setting usually kicks out a 401 error when you try to navigate to sites that resolve to a loopback address e.g.  127.0.0.1
    If ($xmlinput.Configuration.Settings.DisableLoopbackCheck -eq $true)
    {
        WriteLine
        Write-Host -ForegroundColor White " - Disabling Loopback Check..."

        $lsaPath = "HKLM:\System\CurrentControlSet\Control\Lsa"
        $lsaPathValue = Get-ItemProperty -path $lsaPath
        If (-not ($lsaPathValue.DisableLoopbackCheck -eq "1"))
        {
            New-ItemProperty HKLM:\System\CurrentControlSet\Control\Lsa -Name "DisableLoopbackCheck" -value "1" -PropertyType dword -Force | Out-Null
        }
        WriteLine
    }
}

#Region Disable Certificate Revocation List checks
Function DisableCRLCheck([xml]$xmlinput)
{
    WriteLine
    If ($xmlinput.Configuration.Settings.DisableCRLCheck -eq "True")
    {
        Write-Host -ForegroundColor White " - Disabling Certificate Revocation List (CRL) check..."
        Write-Host -ForegroundColor White "  - Registry..."
        New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS -ErrorAction SilentlyContinue | Out-Null
        New-ItemProperty -Path "HKU:\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\WinTrust\Trust Providers\Software Publishing" -Name State -PropertyType DWord -Value 146944 -Force | Out-Null
        New-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\WinTrust\Trust Providers\Software Publishing" -Name State -PropertyType DWord -Value 146944 -Force | Out-Null
        Write-Host -ForegroundColor White "  - Machine.config..."
        ForEach($bitsize in ("","64"))
        {
            # Added a check below for $xml because on Windows Server 2012 machines, the path to $xml doesn't exist until the .Net Framework is installed, so the steps below were failing
            $xml = [xml](Get-Content "$env:windir\Microsoft.NET\Framework$bitsize\v2.0.50727\CONFIG\Machine.config" -ErrorAction SilentlyContinue)
            if ($xml)
            {
                If (!$xml.DocumentElement.SelectSingleNode("runtime")) {
                    $runtime = $xml.CreateElement("runtime")
                    $xml.DocumentElement.AppendChild($runtime) | Out-Null
                }
                If (!$xml.DocumentElement.SelectSingleNode("runtime/generatePublisherEvidence")) {
                    $gpe = $xml.CreateElement("generatePublisherEvidence")
                    $xml.DocumentElement.SelectSingleNode("runtime").AppendChild($gpe) | Out-Null
                }
                $xml.DocumentElement.SelectSingleNode("runtime/generatePublisherEvidence").SetAttribute("enabled","false") | Out-Null
                $xml.Save("$env:windir\Microsoft.NET\Framework$bitsize\v2.0.50727\CONFIG\Machine.config")
            }
            else
            {
                if ($bitsize -eq "") {$bitsize = "32"}
                Write-Warning "$bitsize-bit machine.config not found - could not disable CRL check."
            }
        }
        Write-Host -ForegroundColor White " - Done."
    }
    Else
    {
        Write-Host -ForegroundColor White " - Not changing CRL check behavior."
    }
    WriteLine
}
#EndRegion

#Region Check For or Create Config Files
Function CheckConfigFiles([xml]$xmlinput)
{
    #Region SharePoint config file
    if (Test-Path -Path (Join-Path -Path $env:dp0 -ChildPath $($xmlinput.Configuration.Install.ConfigFile)))
    {
        # Just use the existing config file we found
        $script:configFile = Join-Path -Path $env:dp0 -ChildPath $($xmlinput.Configuration.Install.ConfigFile)
        Write-Host -ForegroundColor White " - Using existing config file:`n - $configFile"
    }
    else
    {
        # Write out a new config file based on defaults and the values provided in $inputFile
        $pidKey = $xmlinput.Configuration.Install.PIDKey
        # Do a rudimentary check on the presence and format of the product key
        if ($pidKey -notlike "?????-?????-?????-?????-?????")
        {
            throw " - The Product ID (PIDKey) is missing or badly formatted.`n - Check the value of <PIDKey> in `"$(Split-Path -Path $inputFile -Leaf)`" and try again."
        }
        $officeServerPremium = $xmlinput.Configuration.Install.SKU -replace "Enterprise","1" -replace "Standard","0"
        $dataDir = $xmlinput.Configuration.Install.DataDir
        # Set it to the default value if it's not specified in $xmlinput
        if ([string]::IsNullOrEmpty($dataDir)) {$dataDir = "%PROGRAMFILES%\Microsoft Office Servers\$env:spVer.0\Data"}
        $xmlConfig = @"
<Configuration>
  <Package Id="sts">
    <Setting Id="LAUNCHEDFROMSETUPSTS" Value="Yes"/>
  </Package>
  <Package Id="spswfe">
    <Setting Id="SETUPCALLED" Value="1"/>
    <Setting Id="OFFICESERVERPREMIUM" Value="$officeServerPremium" />
  </Package>
  <ARP ARPCOMMENTS="Installed with AutoSPInstaller (http://autospinstaller.com)" ARPCONTACT="brian@autospinstaller.com" />
  <Logging Type="verbose" Path="%temp%" Template="SharePoint Server Setup(*).log"/>
  <Display Level="basic" CompletionNotice="No" AcceptEula="Yes"/>
  <INSTALLLOCATION Value="%PROGRAMFILES%\Microsoft Office Servers\" />
  <DATADIR Value="$dataDir"/>
  <PIDKEY Value="$pidKey"/>
  <Setting Id="SERVERROLE" Value="APPLICATION"/>
  <Setting Id="USINGUIINSTALLMODE" Value="1"/>
  <Setting Id="SETUPTYPE" Value="CLEAN_INSTALL"/>
  <Setting Id="SETUP_REBOOT" Value="Never"/>
  <Setting Id="AllowWindowsClientInstall" Value="True"/>
</Configuration>
"@
        $script:configFile = Join-Path -Path (Get-Item $env:TEMP).FullName -ChildPath $($xmlinput.Configuration.Install.ConfigFile)
        Write-Host -ForegroundColor White " - Writing $($xmlinput.Configuration.Install.ConfigFile) to (Get-Item $env:TEMP).FullName..."
        Set-Content -Path "$configFile" -Force -Value $xmlConfig
    }
    #EndRegion

    #Region OWA config file
    if ($xmlinput.Configuration.OfficeWebApps.Install -eq $true)
    {
        if (Test-Path -Path (Join-Path -Path $env:dp0 -ChildPath $($xmlinput.Configuration.OfficeWebApps.ConfigFile)))
        {
            # Just use the existing config file we found
            $script:configFileOWA = Join-Path -Path $env:dp0 -ChildPath $($xmlinput.Configuration.OfficeWebApps.ConfigFile)
            Write-Host -ForegroundColor White " - Using existing OWA config file:`n - $configFileOWA"
        }
        else
        {
            # Write out a new config file based on defaults and the values provided in $inputFile
            $pidKeyOWA = $xmlinput.Configuration.OfficeWebApps.PIDKeyOWA
            # Do a rudimentary check on the presence and format of the product key
            if ($pidKeyOWA -notlike "?????-?????-?????-?????-?????")
            {
                throw " - The OWA Product ID (PIDKey) is missing or badly formatted.`n - Check the value of <PIDKeyOWA> in `"$(Split-Path -Path $inputFile -Leaf)`" and try again."
            }
            $xmlConfigOWA = @"
<Configuration>
	<Package Id="sts">
		<Setting Id="LAUNCHEDFROMSETUPSTS" Value="Yes"/>
	</Package>
    <ARP ARPCOMMENTS="Installed with AutoSPInstaller (http://autospinstaller.com)" ARPCONTACT="brian@autospinstaller.com" />
	<Logging Type="verbose" Path="%temp%" Template="Wac Server Setup(*).log"/>
	<Display Level="basic" CompletionNotice="no" />
	<Setting Id="SERVERROLE" Value="APPLICATION"/>
	<PIDKEY Value="$pidKeyOWA"/>
	<Setting Id="USINGUIINSTALLMODE" Value="1"/>
	<Setting Id="SETUPTYPE" Value="CLEAN_INSTALL"/>
	<Setting Id="SETUP_REBOOT" Value="Never"/>
	<Setting Id="AllowWindowsClientInstall" Value="True"/>
</Configuration>
"@
            $script:configFileOWA = Join-Path -Path (Get-Item $env:TEMP).FullName -ChildPath $($xmlinput.Configuration.OfficeWebApps.ConfigFile)
            Write-Host -ForegroundColor White " - Writing $($xmlinput.Configuration.OfficeWebApps.ConfigFile) to $((Get-Item $env:TEMP).FullName)..."
            Set-Content -Path "$configFileOWA" -Force -Value $xmlConfigOWA
        }
    }
    #EndRegion

    #Region ForeFront answer file
    if (ShouldIProvision($xmlinput.Configuration.ForeFront) -eq $true)
    {
        if (Test-Path -Path (Join-Path -Path $env:dp0 -ChildPath $($xmlinput.Configuration.ForeFront.ConfigFile)))
        {
            # Just use the existing answer file we found
            $script:configFileForeFront = Join-Path -Path $env:dp0 -ChildPath $($xmlinput.Configuration.ForeFront.ConfigFile)
            Write-Host -ForegroundColor White " - Using existing ForeFront answer file:`n - $configFileForeFront"
        }
        else
        {
            $farmAcct = $xmlinput.Configuration.Farm.Account.Username
            $farmAcctPWD = $xmlinput.Configuration.Farm.Account.Password
            # Write out a new answer file based on defaults and the values provided in $inputFile
            $xmlConfigForeFront = @"
<?xml version="1.0" encoding="utf-8"?>
<FSSAnswerFile>
  <AcceptLicense>true</AcceptLicense>
  <AcceptRestart>true</AcceptRestart>
  <AcceptReplacePreviousVS>true</AcceptReplacePreviousVS>
  <InstallType>Full</InstallType>
  <Folders>
    <!--Leave these empty to use the default values-->
    <ProgramFolder></ProgramFolder>
    <DataFolder></DataFolder>
  </Folders>
  <ProxyInformation>
    <UseProxy>false</UseProxy>
    <ServerName></ServerName>
    <Port>80</Port>
    <UserName></UserName>
    <Password></Password>
  </ProxyInformation>
  <SharePointInformation>
    <UserName>$farmAcct</UserName>
    <Password>$farmAcctPWD</Password>
  </SharePointInformation>
  <EnableAntiSpamNow>false</EnableAntiSpamNow>
  <EnableCustomerExperienceImprovementProgram>false</EnableCustomerExperienceImprovementProgram>
</FSSAnswerFile>
"@
            $script:configFileForeFront = Join-Path -Path (Get-Item $env:TEMP).FullName -ChildPath $($xmlinput.Configuration.ForeFront.ConfigFile)
            Write-Host -ForegroundColor White " - Writing $($xmlinput.Configuration.ForeFront.ConfigFile) to $((Get-Item $env:TEMP).FullName)..."
            Set-Content -Path "$configFileForeFront" -Force -Value $xmlConfigForeFront
        }
    }
    #EndRegion
}
#EndRegion

#Region Install Prerequisites
# ===================================================================================
# Func: InstallPrerequisites
# Desc: If Office Web Apps is not already installed install the Prerequisites
# ===================================================================================
Function InstallPrerequisites([xml]$xmlinput)
{
    WriteLine
    $spInstalled = Get-SharePointInstall
    If ($spInstalled)
    {
        Write-Host -ForegroundColor White " - SharePoint $spYear prerequisites appear be already installed - skipping install."
    }
    Else
    {
        Write-Host -ForegroundColor White " - Installing Prerequisite Software:"
        ##If (($env:spVer -eq "14") -and ((Gwmi Win32_OperatingSystem).Version -eq "6.1.7601")) # SP2010 on Win2008 R2 SP1
        If ((Gwmi Win32_OperatingSystem).Version -eq "6.1.7601") # Win2008 R2 SP1
        {
            # Due to the SharePoint 2010 issue described in http://support.microsoft.com/kb/2581903 (related to installing the KB976462 hotfix)
            # (and simply to speed things up for SharePoint 2013) we install the .Net 3.5.1 features prior to attempting the PrerequisiteInstaller on Win2008 R2 SP1
            Write-Host -ForegroundColor White "  - .Net Framework 3.5.1..." -NoNewline
            # Get the current progress preference
            $pref = $ProgressPreference
            # Hide the progress bar since it tends to not disappear
            $ProgressPreference = "SilentlyContinue"
            Import-Module ServerManager
            If (!(Get-WindowsFeature -Name NET-Framework).Installed)
            {
                Add-WindowsFeature -Name NET-Framework | Out-Null
                Write-Host -ForegroundColor White "Done."
            }
            else {Write-Host -ForegroundColor White "Already installed."}
            # Restore progress preference
            $ProgressPreference = $pref

        }
        Try
        {
			# Install prerequisites manually without using PrerequisiteInstaller if we're installing SP2010 on on Windows Server 2012
            if (((Get-WmiObject Win32_OperatingSystem).Version -like "6.2*") -and ($env:spVer -eq "14"))
            {
			    Throw " - SharePoint 2010 is officially unsupported on Windows Server 2012 - see http://support.microsoft.com/kb/2724471"
			}
            else # Install using PrerequisiteInstaller as usual
            {
                If ($xmlinput.Configuration.Install.OfflineInstall -eq $true) # Install all prerequisites from local folder
                {
                    If ($env:spVer -eq "14") # SP2010
                    {
                        Write-Host -ForegroundColor White "  - SQL Native Client..."
                        # Install SQL native client before running pre-requisite installer as newest versions require an IACCEPTSQLNCLILICENSETERMS=YES argument
                        Start-Process "$env:SPbits\PrerequisiteInstallerFiles\sqlncli.msi" -Wait -ArgumentList "/passive /norestart IACCEPTSQLNCLILICENSETERMS=YES"
                        Write-Host -ForegroundColor Blue "  - Running Prerequisite Installer (offline mode)..." -NoNewline
                        $startTime = Get-Date
                        Start-Process "$env:SPbits\PrerequisiteInstaller.exe" -ArgumentList "/unattended `
                                                                                            /SQLNCli:`"$env:SPbits\PrerequisiteInstallerFiles\sqlncli.msi`" `
                                                                                            /ChartControl:`"$env:SPbits\PrerequisiteInstallerFiles\MSChart.exe`" `
                                                                                            /NETFX35SP1:`"$env:SPbits\PrerequisiteInstallerFiles\dotnetfx35.exe`" `
                                                                                            /PowerShell:`"$env:SPbits\PrerequisiteInstallerFiles\Windows6.0-KB968930-x64.msu`" `
                                                                                            /KB976394:`"$env:SPbits\PrerequisiteInstallerFiles\Windows6.0-KB976394-x64.msu`" `
                                                                                            /KB976462:`"$env:SPbits\PrerequisiteInstallerFiles\Windows6.1-KB976462-v2-x64.msu`" `
                                                                                            /IDFX:`"$env:SPbits\PrerequisiteInstallerFiles\Windows6.0-KB974405-x64.msu`" `
                                                                                            /IDFXR2:`"$env:SPbits\PrerequisiteInstallerFiles\Windows6.1-KB974405-x64.msu`" `
                                                                                            /Sync:`"$env:SPbits\PrerequisiteInstallerFiles\Synchronization.msi`" `
                                                                                            /FilterPack:`"$env:SPbits\PrerequisiteInstallerFiles\FilterPack\FilterPack.msi`" `
                                                                                            /ADOMD:`"$env:SPbits\PrerequisiteInstallerFiles\SQLSERVER2008_ASADOMD10.msi`" `
                                                                                            /ReportingServices:`"$env:SPbits\PrerequisiteInstallerFiles\rsSharePoint.msi`" `
                                                                                            /Speech:`"$env:SPbits\PrerequisiteInstallerFiles\SpeechPlatformRuntime.msi`" `
                                                                                            /SpeechLPK:`"$env:SPbits\PrerequisiteInstallerFiles\MSSpeech_SR_en-US_TELE.msi`""
                        If (-not $?) {Throw}
                    }
                    ElseIf ($env:spVer -eq "15") #SP2013
                    {
                        if ((Get-WmiObject Win32_OperatingSystem).Version -like "6.2*") # Try to pre-install .Net Framework 3.5.1 on Windows Server 2012
                        {
                            if (Test-Path -Path "$env:SPbits\PrerequisiteInstallerFiles\sxs")
                            {
                                Write-Host -ForegroundColor White "  - .Net Framework 3.5.1 from `"$env:SPbits\PrerequisiteInstallerFiles\sxs`"..." -NoNewline
                                # Get the current progress preference
                                $pref = $ProgressPreference
                                # Hide the progress bar since it tends to not disappear
                                $ProgressPreference = "SilentlyContinue"
                                Import-Module ServerManager
                                if (!(Get-WindowsFeature -Name NET-Framework-Core).Installed)
                                {
                                    Start-Process -FilePath DISM.exe -ArgumentList "/Online /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:`"$env:SPbits\PrerequisiteInstallerFiles\sxs`"" -NoNewWindow -Wait
                                    ##Install-WindowsFeature NET-Framework-Core –Source "$env:SPbits\PrerequisiteInstallerFiles\sxs" | Out-Null
                                    Write-Host -ForegroundColor White "Done."
                                }
                                else {Write-Host -ForegroundColor White "Already installed."}
                                # Restore progress preference
                                $ProgressPreference = $pref
                            }
                            else {Write-Host -ForegroundColor White " - Could not locate source for .Net Framework 3.5.1`n - The PrerequisiteInstaller will attempt to download it."}
                        }
                        Write-Host -ForegroundColor Blue "  - Running Prerequisite Installer (offline mode)..." -NoNewline
                        $startTime = Get-Date
                        Start-Process "$env:SPbits\PrerequisiteInstaller.exe" -ArgumentList "/unattended `
                                                                                             /SQLNCli:`"$env:SPbits\PrerequisiteInstallerFiles\sqlncli.msi`" `
                                                                                             /PowerShell:`"$env:SPbits\PrerequisiteInstallerFiles\Windows6.1-KB2506143-x64.msu`" `
                                                                                             /NETFX:`"$env:SPbits\PrerequisiteInstallerFiles\dotNetFx45_Full_x86_x64.exe`" `
                                                                                             /IDFX:`"$env:SPbits\PrerequisiteInstallerFiles\Windows6.1-KB974405-x64.msu`" `
                                                                                             /IDFX11:`"$env:SPbits\PrerequisiteInstallerFiles\MicrosoftIdentityExtensions-64.msi`" `
                                                                                             /Sync:`"$env:SPbits\PrerequisiteInstallerFiles\Synchronization.msi`" `
                                                                                             /AppFabric:`"$env:SPbits\PrerequisiteInstallerFiles\WindowsServerAppFabricSetup_x64.exe`" `
                                                                                             /KB2671763:`"$env:SPbits\PrerequisiteInstallerFiles\AppFabric1.1-RTM-KB2671763-x64-ENU.exe`" `
                                                                                             /MSIPCClient:`"$env:SPbits\PrerequisiteInstallerFiles\setup_msipc_x64.msi`" `
                                                                                             /WCFDataServices:`"$env:SPbits\PrerequisiteInstallerFiles\WcfDataServices.exe`""
                        If (-not $?) {Throw}
                    }
                }
                Else # Regular prerequisite install - download required files
                {
                    Write-Host -ForegroundColor Blue "  - Running Prerequisite Installer (online mode)..." -NoNewline
                    $startTime = Get-Date
                    Start-Process "$env:SPbits\PrerequisiteInstaller.exe" -ArgumentList "/unattended" -WindowStyle Minimized
                    If (-not $?) {Throw}
                }
                Show-Progress -Process PrerequisiteInstaller -Color Blue -Interval 5
                $delta,$null = (New-TimeSpan -Start $startTime -End (Get-Date)).ToString() -split "\."
                Write-Host -ForegroundColor White "  - Prerequisite Installer completed in $delta."
            }
            If ($env:spVer -eq "15") # SP2013
            {
                # Install the "missing prerequisites" for SP2013 per http://www.toddklindt.com/blog/Lists/Posts/Post.aspx?ID=349
                Write-Host -ForegroundColor White "  - SharePoint 2013 `"missing hotfix`" prerequisites..."
                # Expand hotfix executable to $env:SPbits\PrerequisiteInstallerFiles\
                if ((Gwmi Win32_OperatingSystem).Version -eq "6.1.7601") # Win2008 R2 SP1
                {
                    $missingHotfixes = @{"Windows6.1-KB2554876-v2-x64.msu" = "http://hotfixv4.microsoft.com/Windows%207/Windows%20Server2008%20R2%20SP1/sp2/Fix368051/7600/free/433385_intl_x64_zip.exe";
					    			     "Windows6.1-KB2708075-x64.msu" = "http://hotfixv4.microsoft.com/Windows%207/Windows%20Server2008%20R2%20SP1/sp2/Fix402568/7600/free/447698_intl_x64_zip.exe";
                                         "Windows6.1-KB2472264-v3-x64.msu" = "http://hotfixv4.microsoft.com/Windows%207/Windows%20Server2008%20R2%20SP1/sp2/Fix354400/7600/free/427087_intl_x64_zip.exe";
                                         "Windows6.1-KB2567680-x64.msu" = "http://download.microsoft.com/download/C/D/A/CDAF5DD8-3B9A-4F8D-A48F-BEFE53C5B249/Windows6.1-KB2567680-x64.msu";
                                         "NDP45-KB2759112-x64.exe" = "http://download.microsoft.com/download/5/6/3/5631B753-A009-48AF-826C-2D2C29B94172/NDP45-KB2759112-x64.exe"}
                }
                elseif ((Get-WmiObject Win32_OperatingSystem).Version -like "6.2*") # Win2012
                {
                    $missingHotfixes = @{"Windows8-RT-KB2765317-x64.msu" = "http://download.microsoft.com/download/0/2/E/02E9E569-5462-48EB-AF57-8DCCF852E6F4/Windows8-RT-KB2765317-x64.msu"}
                }
                $hotfixLocation = $env:SPbits+"\PrerequisiteInstallerFiles"
                ForEach ($hotfixPatch in $missingHotfixes.Keys)
                {
                    $hotfixKB = $hotfixPatch.Split('-') | Where-Object {$_ -like "KB*"}
                    # Check if the hotfix is already installed
                    Write-Host -ForegroundColor White "   - Checking for $hotfixKB..." -NoNewline
                    If (!(Get-HotFix -Id $hotfixKB -ErrorAction SilentlyContinue))
                    {
                        Write-Host -ForegroundColor White "Missing; attempting to install..."
                        $hotfixUrl = $missingHotfixes.$hotfixPatch
                        $hotfixFile = $hotfixUrl.Split('/')[-1]
                        $hotfixFileZip = $hotfixFile+".zip"
                        $hotfixZipPath = Join-Path -Path $hotfixLocation -ChildPath $hotfixFileZip
                        # Check if the .msu/.exe file is already present
                        If (Test-Path "$hotfixLocation\$hotfixPatch")
                        {
                            Write-Host -ForegroundColor White "    - Hotfix file `"$hotfixPatch`" found."
                        }
                        Else
                        {
                            # Check if the downloaded package exists with a .zip extension
                            If (!([string]::IsNullOrEmpty($hotfixFileZip)) -and (Test-Path "$hotfixLocation\$hotfixFileZip"))
                            {
                                Write-Host -ForegroundColor White "    - File $hotfixFile (zip) found."
                            }
                        	Else
                            {
                                # Check if the downloaded package exists
                                If (Test-Path "$hotfixLocation\$hotfixFile")
                            	{
                            		Write-Host -ForegroundColor White "    - File $hotfixFile found."
                            	}
                                Else # Go ahead and download the missing package
                            	{
                                    Try
                                    {
                                		# Begin download
                                        Write-Host -ForegroundColor White "    - Hotfix $hotfixPatch not found in $env:SPbits\PrerequisiteInstallerFiles"
                                        Write-Host -ForegroundColor White "    - Attempting to download..." -NoNewline
                                        Import-Module BitsTransfer | Out-Null
                                        Start-BitsTransfer -Source $hotfixUrl -Destination "$hotfixLocation\$hotfixFile" -DisplayName "Downloading `'$hotfixFile`' to $hotfixLocation" -Priority Foreground -Description "From $hotfixUrl..." -ErrorVariable err
                                        if ($err) {Write-Host "."; Throw "  - Could not download from $hotfixUrl!"}
                                        Write-Host -ForegroundColor White "Done!"
                                	}
                                    Catch
                                    {
                                    	Write-Warning "  - An error occurred attempting to download `"$hotfixFile`"."
                                    	break
                                    }
                                }
                                if ($hotfixFile -like "*zip.exe") # The hotfix is probably a self-extracting exe
                                {
                                    # Give the file a .zip extension so we can work with it like a compressed folder
                                    Write-Host -ForegroundColor White "    - Renaming $hotfixFile to $hotfixFileZip..."
                                    Rename-Item -Path "$hotfixLocation\$hotfixFile" -NewName $hotfixFileZip -Force -ErrorAction SilentlyContinue
                                }
                            }
                            If (Test-Path "$hotfixLocation\$hotfixFileZip") # The zipped hotfix exists, ands needs to be extracted
                            {
                                Write-Host -ForegroundColor White "    - Extracting `"$hotfixPatch`" from `"$hotfixFile`"..." -NoNewline
                                $shell = New-Object -ComObject Shell.Application
                                $hotfixFileZipNs = $shell.Namespace($hotfixZipPath)
                                $hotfixLocationNs = $shell.Namespace($hotfixLocation)
                                $hotfixLocationNs.Copyhere($hotfixFileZipNs.items())
                                Write-Host -ForegroundColor White "Done."
                            }
                        }
                        # Install the hotfix
                        $extractedHotfixPath = Join-Path -Path $hotfixLocation -ChildPath $hotfixPatch
                        Write-Host -ForegroundColor White "    - Installing hotfix $hotfixPatch..." -NoNewline
                        if ($hotfixPatch -like "*.msu") # Treat as a Windows Update patch
                        {
                            Start-Process -FilePath "wusa.exe" -ArgumentList "`"$extractedHotfixPath`" /quiet /norestart" -Wait -NoNewWindow
                        }
                        else # Treat as an executable (.exe) patch
                        {
                            Start-Process -FilePath "$extractedHotfixPath" -ArgumentList "/passive /norestart" -Wait -NoNewWindow
                        }
                        Write-Host -ForegroundColor White "Done."
                    }
                    Else {Write-Host -ForegroundColor White "Already installed."}
                }
            }
        }
        Catch
        {
            Write-Host -ForegroundColor Blue "."
            Write-Host -ForegroundColor Red " - Error: $_ $LASTEXITCODE"
            If ($LASTEXITCODE -eq "1") {Throw " - Another instance of this application is already running"}
            ElseIf ($LASTEXITCODE -eq "2") {Throw " - Invalid command line parameter(s)"}
            ElseIf ($LASTEXITCODE -eq "1001") {Throw " - A pending restart blocks installation"}
            ElseIf ($LASTEXITCODE -eq "3010") {Throw " - A restart is needed"}
            ElseIf ($LASTEXITCODE -eq "-2145124329") {Write-Host -ForegroundColor White " - A known issue occurred installing one of the prerequisites"; InstallPreRequisites ([xml]$xmlinput)}
            Else {Throw " - An unknown error occurred installing prerequisites"}
        }
        # Parsing most recent PreRequisiteInstaller log for errors or restart requirements, since $LASTEXITCODE doesn't seem to work...
        $preReqLog = Get-ChildItem -Path (Get-Item $env:TEMP).FullName | ? {$_.Name -like "PrerequisiteInstaller.*"} | Sort-Object -Descending -Property "LastWriteTime" | Select-Object -first 1
        If ($preReqLog -eq $null)
        {
            Write-Warning "Could not find PrerequisiteInstaller log file"
        }
        Else
        {
            # Get error(s) from log
            $preReqLastError = $preReqLog | Select-String -SimpleMatch -Pattern "Error" -Encoding Unicode | ? {$_.Line  -notlike "*Startup task*"}
            If ($preReqLastError)
            {
                ForEach ($preReqError in ($preReqLastError | ForEach {$_.Line})) {Write-Warning $preReqError}
                $preReqLastReturncode = $preReqLog | Select-String -SimpleMatch -Pattern "Last return code" -Encoding Unicode | Select-Object -Last 1
                If ($preReqLastReturnCode) {Write-Warning $preReqLastReturncode.Line}
                If (($preReqLastReturncode -like "*-2145124329*") -or ($preReqLastReturncode -like "*2359302*") -or ($preReqLastReturncode -eq "5"))
                {
                    Write-Host -ForegroundColor White " - A known issue occurred installing one of the prerequisites - retrying..."
                    InstallPreRequisites ([xml]$xmlinput)
                }
                ElseIf ($preReqLog | Select-String -SimpleMatch -Pattern "Error when enabling ASP.NET v4.0.30319" -Encoding Unicode)
                {
                    # Account for new issue with Win2012 RC and SP2013
                    Write-Host -ForegroundColor White " - A known issue occurred configuring .NET 4 / IIS."
                    $preReqKnownIssueRestart = $true
                }
                ElseIf ($preReqLog | Select-String -SimpleMatch -Pattern "pending restart blocks the installation" -Encoding Unicode)
                {
                    Write-Host -ForegroundColor White " - A pending restart blocks the installation."
                    $preReqKnownIssueRestart = $true
                }
                Else
                {
                    Invoke-Item -Path "$((Get-Item $env:TEMP).FullName)\$preReqLog"
                    Throw " - Review the log file and try to correct any error conditions."
                }
            }
            # Look for restart requirement in log
            $preReqRestartNeeded = ($preReqLog | Select-String -SimpleMatch -Pattern "0XBC2=3010" -Encoding Unicode) -or ($preReqLog | Select-String -SimpleMatch -Pattern "0X3E9=1001" -Encoding Unicode)
            If ($preReqRestartNeeded -or $preReqKnownIssueRestart)
            {
                Write-Host -ForegroundColor White " - Setting AutoSPInstaller information in the registry..."
                New-Item -Path "HKLM:\SOFTWARE\AutoSPInstaller\" -ErrorAction SilentlyContinue | Out-Null
				$regKey = Get-Item -Path "HKLM:\SOFTWARE\AutoSPInstaller\"
                $regKey | New-ItemProperty -Name "RestartRequired" -PropertyType String -Value "1" -Force | Out-Null
                # We now also want to disable remote installs, or else each server will attempt to remote install to every *other* server after it reboots!
                $regKey | New-ItemProperty -Name "CancelRemoteInstall" -PropertyType String -Value "1" -Force | Out-Null
				$regKey | New-ItemProperty -Name "LogTime" -PropertyType String -Value $script:Logtime -ErrorAction SilentlyContinue | Out-Null
                Throw " - One or more of the prerequisites requires a restart."
            }
            Write-Host -ForegroundColor White " - All Prerequisite Software installed successfully."
        }
    }
    WriteLine
}
#EndRegion

#Region Provision IIS Server Role
Function ProvisionIISServerRole
{
	WriteLine
	Write-Host -ForegroundColor White " - Provisioning IIS Server Role:"
	If ((Gwmi Win32_OperatingSystem).Version -eq "6.1.7601") # Windows Server 2008 R2 SP1
	{
		Write-Host -ForegroundColor White " - Detected Software: Windows 2008 R2 SP1"
		Write-Host -ForegroundColor White " - Importing Module Server Manager ..."
		Import-Module ServerManager
		Write-Host -ForegroundColor White " - Adding Role and Features : Web Server IIS Role and Ink Support Feature ..."
		Add-WindowsFeature Web-Server,Web-WebServer,Web-Common-Http,Web-Static-Content,Web-App-Dev,Web-Asp-Net,Web-Net-Ext,Web-ISAPI-Ext,Web-ISAPI-Filter,Web-Includes,Web-Security,Web-Windows-Auth,Web-Filtering,Web-Stat-Compression,Web-Dyn-Compression,Web-Mgmt-Console,Ink-Handwriting,IH-Ink-Support
		Write-Host -ForegroundColor Green " - IIS Server Role Successfully Provionned"
	}
	ElseIf ((Get-WmiObject Win32_OperatingSystem).Version -like "6.2*") # Windows Server 2012
	{
		Write-Host -ForegroundColor White " - Detected Software: Windows 2012"
		
		#Detect the source path
		
		Write-Host -ForegroundColor White " - Adding Role and Features : Web Server IIS Role and Ink Support Feature"
		Add-WindowsFeature Web-Server,Web-Mgmt-Tools,Web-Mgmt-Console,Web-WebServer,Web-Common-Http,Web-Default-Doc,Web-Static-Content,Web-Performance,Web-Stat-Compression,Web-Dyn-Compression,Web-Security,Web-Filtering,Web-Windows-Auth,Web-App-Dev,Web-Net-Ext45,Web-Asp-Net45,Web-ISAPI-Ext,Web-ISAPI-Filter,Web-Includes,InkandHandwritingServices,NET-Framework-Features,NET-Framework-Core
		Write-Host -ForegroundColor Green " - IIS Server Role Successfully Provionned"
	}
}
#EndRegion

#Region Install SharePoint
# ===================================================================================
# Func: InstallSharePoint
# Desc: Installs the SharePoint binaries in unattended mode
# ===================================================================================
Function InstallSharePoint([xml]$xmlinput)
{
    WriteLine
    # Create a hash table with major version to product year mappings
    $spYears = @{"14" = "2010"; "15" = "2013"}
    $spYear = $spYears.$env:spVer
    $spInstalled = Get-SharePointInstall
    If ($spInstalled)
    {
        Write-Host -ForegroundColor White " - SharePoint $spYear binaries appear to be already installed - skipping installation."
    }
    Else
    {
        # Install SharePoint Binaries
        If (Test-Path "$env:SPbits\setup.exe")
        {
            Write-Host -ForegroundColor Blue " - Installing SharePoint $spYear binaries..." -NoNewline
            $startTime = Get-Date
            Start-Process "$env:SPbits\setup.exe" -ArgumentList "/config `"$configFile`"" -WindowStyle Minimized
            Show-Progress -Process setup -Color Blue -Interval 5
            $delta,$null = (New-TimeSpan -Start $startTime -End (Get-Date)).ToString() -split "\."
            Write-Host -ForegroundColor White " - SharePoint $spYear setup completed in $delta."
            If (-not $?)
            {
                Throw " - Error $LASTEXITCODE occurred running $env:SPbits\setup.exe"
            }

            # Parsing most recent SharePoint Server Setup log for errors or restart requirements, since $LASTEXITCODE doesn't seem to work...
            $setupLog = Get-ChildItem -Path (Get-Item $env:TEMP).FullName | ? {$_.Name -like "SharePoint Server Setup*"} | Sort-Object -Descending -Property "LastWriteTime" | Select-Object -first 1
            If ($setupLog -eq $null)
            {
                Throw " - Could not find SharePoint Server Setup log file!"
            }

            # Get error(s) from log
            $setupLastError = $setupLog | Select-String -SimpleMatch -Pattern "Error:" | Select-Object -Last 1
            $setupSuccess = $setupLog | Select-String -SimpleMatch -Pattern "Successfully installed package: oserver"
            If ($setupLastError -and !$setupSuccess)
            {
                Write-Warning $setupLastError.Line
                Invoke-Item -Path "$((Get-Item $env:TEMP).FullName)\$setupLog"
                Throw " - Review the log file and try to correct any error conditions."
            }
            # Look for restart requirement in log
            $setupRestartNotNeeded = $setupLog | select-string -SimpleMatch -Pattern "System reboot is not pending."
            If (!($setupRestartNotNeeded))
            {
                Throw " - SharePoint setup requires a restart. Run the script again after restarting to continue."
            }

            Write-Host -ForegroundColor Blue " - Waiting for SharePoint Products and Technologies Wizard to launch..." -NoNewline
            While ((Get-Process |?{$_.ProcessName -like "psconfigui*"}) -eq $null)
            {
                Write-Host -ForegroundColor Blue "." -NoNewline
                Start-Sleep 1
            }
            Write-Host -ForegroundColor Blue "Done."
            Write-Host -ForegroundColor White " - Exiting Products and Technologies Wizard - using Powershell instead!"
            Stop-Process -Name psconfigui
        }
        Else
        {
            Throw " - Install path $env:SPbits not found!!"
        }
    }
    WriteLine
}
#EndRegion

#Region Install Office Web Apps 2010
# ===================================================================================
# Func: InstallOfficeWebApps2010
# Desc: Installs the OWA binaries in unattended mode
# From: Ported over by user http://www.codeplex.com/site/users/view/cygoh originally from the InstallSharePoint function, fixed up by brianlala
# Originally posted on: http://autospinstaller.codeplex.com/discussions/233530
# ===================================================================================
Function InstallOfficeWebApps2010([xml]$xmlinput)
{
    If ($xmlinput.Configuration.OfficeWebApps.Install -eq $true -and $env:spVer -eq "14") # Check for SP2010
    {
        WriteLine
        If (Test-Path "$env:CommonProgramFiles\Microsoft Shared\Web Server Extensions\$env:spVer\TEMPLATE\FEATURES\OfficeWebApps\feature.xml") # Crude way of checking if Office Web Apps is already installed
        {
            Write-Host -ForegroundColor White " - Office Web Apps binaries appear to be already installed - skipping install."
        }
        Else
        {
            $spYears = @{"14" = "2010"; "15" = "2013"}
            $spYear = $spYears.$env:spVer
            # Install Office Web Apps Binaries
            If (Test-Path "$bits\$spYear\OfficeWebApps\setup.exe")
            {
                Write-Host -ForegroundColor Blue " - Installing Office Web Apps binaries..." -NoNewline
                $startTime = Get-Date
                Start-Process "$bits\$spYear\OfficeWebApps\setup.exe" -ArgumentList "/config `"$configFileOWA`"" -WindowStyle Minimized
                Show-Progress -Process setup -Color Blue -Interval 5
                $delta,$null = (New-TimeSpan -Start $startTime -End (Get-Date)).ToString() -split "\."
                Write-Host -ForegroundColor White " - Office Web Apps setup completed in $delta."
                If (-not $?) {
                    Throw " - Error $LASTEXITCODE occurred running $bits\$spYear\OfficeWebApps\setup.exe"
                }
                # Parsing most recent Office Web Apps Setup log for errors or restart requirements, since $LASTEXITCODE doesn't seem to work...
                $setupLog = Get-ChildItem -Path (Get-Item $env:TEMP).FullName | ? {$_.Name -like "Wac Server Setup*"} | Sort-Object -Descending -Property "LastWriteTime" | Select-Object -first 1
                If ($setupLog -eq $null)
                {
                    Throw " - Could not find Office Web Apps Setup log file!"
                }
                # Get error(s) from log
                $setupLastError = $setupLog | select-string -SimpleMatch -Pattern "Error:" | Select-Object -Last 1 #| ? {$_.Line -notlike "*Startup task*"}
                If ($setupLastError)
                {
                    Write-Warning $setupLastError.Line
                    Invoke-Item -Path "$((Get-Item $env:TEMP).FullName)\$setupLog"
                    Throw " - Review the log file and try to correct any error conditions."
                }
                # Look for restart requirement in log
                $setupRestartNotNeeded = $setupLog | select-string -SimpleMatch -Pattern "System reboot is not pending."
                If (!($setupRestartNotNeeded))
                {
                    Throw " - Office Webapps setup requires a restart. Run the script again after restarting to continue."
                }
                Write-Host -ForegroundColor Blue " - Waiting for SharePoint Products and Technologies Wizard to launch..." -NoNewline
                While ((Get-Process |?{$_.ProcessName -like "psconfigui*"}) -eq $null)
                {
                    Write-Host -ForegroundColor Blue "." -NoNewline
                    Start-Sleep 1
                }
                # The Connect-SPConfigurationDatabase cmdlet throws an error about an "upgrade required" if we don't at least *launch* the Wizard, so we wait to let it launch, then kill it.
                Start-Sleep 10
                Write-Host -ForegroundColor Blue "Done."
                Write-Host -ForegroundColor White " - Exiting Products and Technologies Wizard - using Powershell instead!"
                Stop-Process -Name psconfigui
            }
            Else
            {
                Throw " - Install path $bits\$spYear\OfficeWebApps not found!!"
            }
        }
        WriteLine
    }
}
#EndRegion

#Region Configure Office Web Apps 2010
Function ConfigureOfficeWebApps([xml]$xmlinput)
{
    If ($xmlinput.Configuration.OfficeWebApps.Install -eq $true -and $env:spVer -eq "14") # Check for SP2010
    {
        Writeline
        Try
        {
            Write-Host -ForegroundColor White " - Configuring Office Web Apps..."
            # Install Help Files
            Write-Host -ForegroundColor White " - Installing Help Collection..."
            Install-SPHelpCollection -All
            ##WaitForHelpInstallToFinish
            # Install application content
            Write-Host -ForegroundColor White " - Installing Application Content..."
            Install-SPApplicationContent
            # Secure resources
            Write-Host -ForegroundColor White " - Securing Resources..."
            Initialize-SPResourceSecurity
            # Install Services
            Write-Host -ForegroundColor White " - Installing Services..."
            Install-SPService
            If (!$?) {Throw}
            # Install (all) features
            Write-Host -ForegroundColor White " - Installing Features..."
            $features = Install-SPFeature -AllExistingFeatures -Force
        }
        Catch
        {
            Write-Output $_
            Throw " - Error configuring Office Web Apps!"
        }
        Writeline
    }
}
#EndRegion

#Region Install Language Packs
# ===================================================================================
# Func: Install Language Packs
# Desc: Install language packs and report on any languages installed
# ===================================================================================
Function InstallLanguagePacks([xml]$xmlinput)
{
    WriteLine
    $spYears = @{"14" = "2010"; "15" = "2013"}
    $spYear = $spYears.$env:spVer
    #Get installed languages from registry (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\$env:spVer.0\InstalledLanguages)
    $installedOfficeServerLanguages = (Get-Item "HKLM:\Software\Microsoft\Office Server\$env:spVer.0\InstalledLanguages").GetValueNames() | ? {$_ -ne ""}   # Look for extracted language packs
    $extractedLanguagePacks = (Get-ChildItem "$bits\$spYear\LanguagePacks" -Name -Include "??-??" -ErrorAction SilentlyContinue)
    $serverLanguagePacks = (Get-ChildItem "$bits\$spYear\LanguagePacks" -Name -Include ServerLanguagePack_*.exe -ErrorAction SilentlyContinue)
    If ($extractedLanguagePacks)
    {
        Write-Host -ForegroundColor White " - Installing SharePoint Language Packs:"
        ForEach ($languagePackFolder in $extractedLanguagePacks)
        {
            $language = $installedOfficeServerLanguages | ? {$_ -eq $languagePackFolder}
            If (!$language)
            {
                Write-Host -ForegroundColor Blue " - Installing extracted language pack $languagePackFolder..." -NoNewline
                $startTime = Get-Date
                Start-Process -WorkingDirectory "$bits\$spYear\LanguagePacks\$languagePackFolder\" -FilePath "setup.exe" -ArgumentList "/config $bits\$spYear\LanguagePacks\$languagePackFolder\Files\SetupSilent\config.xml"
                Show-Progress -Process setup -Color Blue -Interval 5
                $delta,$null = (New-TimeSpan -Start $startTime -End (Get-Date)).ToString() -split "\."
                Write-Host -ForegroundColor White " - Language pack $languagePackFolder setup completed in $delta."
            }
        }
        Write-Host -ForegroundColor White " - Language Pack installation complete."
    }
    # Look for Server language pack installers
    ElseIf ($serverLanguagePacks)
    {
        Write-Host -ForegroundColor White " - Installing SharePoint Language Packs:"
        ForEach ($languagePack in $serverLanguagePacks)
        {
            # Slightly convoluted check to see if language pack is already installed, based on name of language pack file.
            # This only works if you've renamed your language pack(s) to follow the convention "ServerLanguagePack_XX-XX.exe" where <XX-XX> is a culture such as <en-us>.
            $language = $installedOfficeServerLanguages | ? {$_ -eq (($languagePack -replace "ServerLanguagePack_","") -replace ".exe","")}
            If (!$language)
            {
                Write-Host -ForegroundColor Blue " - Installing $languagePack..." -NoNewline
                $startTime = Get-Date
                Start-Process -FilePath "$bits\$spYear\LanguagePacks\$languagePack" -ArgumentList "/quiet /norestart"
                Show-Progress -Process $($languagePack -replace ".exe", "") -Color Blue -Interval 5
                $delta,$null = (New-TimeSpan -Start $startTime -End (Get-Date)).ToString() -split "\."
                Write-Host -ForegroundColor White " - Language pack $languagePack setup completed in $delta."
                $language = (($languagePack -replace "ServerLanguagePack_","") -replace ".exe","")
                # Install Foundation Language Pack SP1, then Server Language Pack SP1, if found
                If (Get-ChildItem "$bits\$spYear\LanguagePacks" -Name -Include spflanguagepack2010sp1-kb2460059-x64-fullfile-$language.exe -ErrorAction SilentlyContinue)
                {
                    Write-Host -ForegroundColor Blue " - Installing Foundation language pack SP1 for $language..." -NoNewline
                    Start-Process -WorkingDirectory "$bits\$spYear\LanguagePacks\" -FilePath "spflanguagepack2010sp1-kb2460059-x64-fullfile-$language.exe" -ArgumentList "/quiet /norestart"
                    Show-Progress -Process spflanguagepack2010sp1-kb2460059-x64-fullfile-$language -Color Blue -Interval 5
                    # Install Server Language Pack SP1, if found
                    If (Get-ChildItem "$bits\$spYear\LanguagePacks" -Name -Include serverlanguagepack2010sp1-kb2460056-x64-fullfile-$language.exe -ErrorAction SilentlyContinue)
                    {
                        Write-Host -ForegroundColor Blue " - Installing Server language pack SP1 for $language..." -NoNewline
                        Start-Process -WorkingDirectory "$bits\$spYear\LanguagePacks\" -FilePath "serverlanguagepack2010sp1-kb2460056-x64-fullfile-$language.exe" -ArgumentList "/quiet /norestart"
                        Show-Progress -Process serverlanguagepack2010sp1-kb2460056-x64-fullfile-$language -Color Blue -Interval 5
                    }
                    Else
                    {
                        Write-Warning "Server Language Pack SP1 not found for $language!"
                        Write-Warning "You must install it for the language service pack patching process to be complete."
                    }
                }
                Else {Write-Host -ForegroundColor White " - No Language Pack service packs found."}
            }
            Else
            {
                Write-Host -ForegroundColor White " - Language $language already appears to be installed, skipping."
            }
        }
        Write-Host -ForegroundColor White " - Language Pack installation complete."
    }
    Else
    {
        Write-Host -ForegroundColor White " - No language packs found in $bits\$spYear\LanguagePacks, skipping."
    }

    # Get and note installed languages
    $installedOfficeServerLanguages = (Get-Item "HKLM:\Software\Microsoft\Office Server\$env:spVer.0\InstalledLanguages").GetValueNames() | ? {$_ -ne ""}
    Write-Host -ForegroundColor White " - Currently installed languages:"
    ForEach ($language in $installedOfficeServerLanguages)
    {
        Write-Host "  -" ([System.Globalization.CultureInfo]::GetCultureInfo($language).DisplayName)
    }
    WriteLine
}
#EndRegion

# ===================================================================================
# Func: ConfigureIISLogging
# Desc: Configures IIS Logging for the local server
# ===================================================================================
Function ConfigureIISLogging([xml]$xmlinput)
{
    WriteLine
    $IISLogConfig = $xmlinput.Configuration.Farm.Logging.IISLogs
    Write-Host -ForegroundColor White " - Configuring IIS logging..."
    If (!([string]::IsNullOrEmpty($IISLogConfig.Path)))
    {
        $IISLogDir = $IISLogConfig.Path
        EnsureFolder $IISLogDir
        ImportWebAdministration
        $oldIISLogDir = Get-WebConfigurationProperty "/system.applicationHost/sites/siteDefaults" -name logfile.directory.Value
        $oldIISLogDir = $oldIISLogDir -replace ("%SystemDrive%","$env:SystemDrive")
        If ($IISLogDir -ne $oldIISLogDir) # Only change the global IIS logging location if the desired location is different than the current
        {
            Write-Host -ForegroundColor White " - Setting the global IIS logging location..."
            # The line below is from http://stackoverflow.com/questions/4626791/powershell-command-to-set-iis-logging-settings
            Set-WebConfigurationProperty "/system.applicationHost/sites/siteDefaults" -name logfile.directory -value $IISLogDir
            If (Test-Path -Path $oldIISLogDir)
            {
                Write-Host -ForegroundColor White " - Moving any contents in old location $oldIISLogDir to $IISLogDir..."
                ForEach ($item in $(Get-ChildItem $oldIISLogDir))
                {
                    Move-Item -Path $oldIISLogDir\$item -Destination $IISLogDir -Force -ErrorAction SilentlyContinue
                }
            }
        }
    }
    Else # Assume default value if none was specified in the XML input file
    {
        $IISLogDir = "$env:SystemDrive\Inetpub\logs" # We omit the trailing \LogFiles so we can compress the entire \logs\ folder including Failed Requests etc.
    }
    # Finally, enable NTFS compression on the IIS log location to save disk space
    If ($IISLogConfig.Compress -eq $true)
    {
        CompressFolder $IISLogDir
    }
    WriteLine
}

Function Install-NetFramework ($server, $password)
{
	If ($password) {$credential = New-Object System.Management.Automation.PsCredential $env:USERDOMAIN\$env:USERNAME,$(ConvertTo-SecureString $password)}
    If (!$credential) {$credential = $host.ui.PromptForCredential("AutoSPInstaller - Remote Install", "Re-Enter Credentials for Remote Authentication:", "$env:USERDOMAIN\$env:USERNAME", "NetBiosUserName")}
    If ($session.Name -ne "AutoSPInstallerSession-$server")
    {
        Write-Host -ForegroundColor White " - Starting remote session to $server..."
        $session = New-PSSession -Name "AutoSPInstallerSession-$server" -Authentication Credssp -Credential $credential -ComputerName $server
    }
    $remoteQueryOS = Invoke-Command -ScriptBlock {Get-WmiObject Win32_OperatingSystem} -Session $session
	If (!($remoteQueryOS.Version.Contains("6.2"))) # Only perform the stuff below if we aren't on Windows 2012
	{
	    Write-Host -ForegroundColor White " - Pre-installing .Net Framework feature on $server..."
	    Invoke-Command -ScriptBlock {Import-Module ServerManager | Out-Null
	                                # Get the current progress preference
	                                $pref = $ProgressPreference
	                                # Hide the progress bar since it tends to not disappear
	                                $ProgressPreference = "SilentlyContinue"
	                                Import-Module ServerManager
	                                If (!(Get-WindowsFeature -Name NET-Framework).Installed) {Add-WindowsFeature -Name NET-Framework | Out-Null}
	                                # Restore progress preference
	                                $ProgressPreference = $pref} -Session $session
	}
}

#Region Miscellaneous/Utility Functions
# ===================================================================================
# Func: Pause
# Desc: Wait for user to press a key - normally used after an error has occured or input is required
# ===================================================================================
Function Pause($action, $key)
{
    # From http://www.microsoft.com/technet/scriptcenter/resources/pstips/jan08/pstip0118.mspx
    if ($key -eq "any" -or ([string]::IsNullOrEmpty($key)))
    {
        $actionString = "Press any key to $action..."
        if (-not $unattended)
        {
            Write-Host $actionString
            $null = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
        }
        else
        {
            Write-Host "Skipping pause due to -unattended switch: $actionString"
        }
    }
    else
    {
        $actionString = "Enter `"$key`" to $action"
        $continue = Read-Host -Prompt $actionString
        if ($continue -ne $key) {pause $action $key}

    }
}

# ====================================================================================
# Func: WriteLine
# Desc: Writes a nice line of dashes across the screen
# ====================================================================================
Function WriteLine
{
    Write-Host -ForegroundColor White "--------------------------------------------------------------"
}

# ====================================================================================
# Func: PinToTaskbar
# Desc: Pins a program to the taskbar
# From: http://techibee.com/powershell/pin-applications-to-task-bar-using-powershell/685
# ====================================================================================
Function PinToTaskbar([string]$application)
{
    $shell = New-Object -ComObject "Shell.Application"
    $folder = $shell.Namespace([System.IO.Path]::GetDirectoryName($application))

    Foreach ($verb in $folder.ParseName([System.IO.Path]::GetFileName($application)).verbs())
    {
        If($verb.name.replace("&","") -match "Pin to Taskbar")
        {
            $verb.DoIt()
        }
    }
}

# ====================================================================================
# Func: Get-AdministratorsGroup
# Desc: Returns the actual (localized) name of the built-in Administrators group
# From: Proposed by Codeplex user Sheppounet at http://autospinstaller.codeplex.com/discussions/265749
# ====================================================================================

Function Get-AdministratorsGroup
{
    If(!$builtinAdminGroup)
    {
        $builtinAdminGroup = (Get-WmiObject -Class Win32_Group -computername $env:COMPUTERNAME -Filter "SID='S-1-5-32-544' AND LocalAccount='True'" -errorAction "Stop").Name
    }
    Return $builtinAdminGroup
}

# ====================================================================================
# Func: Confirm-LocalSession
# Desc: Returns $false if we are running over a PS remote session, $true otherwise
# From: Brian Lalancette, 2012
# ====================================================================================
Function Confirm-LocalSession
{
    # Another way
    # If ((Get-Process -Id $PID).ProcessName -eq "wsmprovhost") {Return $false}
    If ($Host.Name -eq "ServerRemoteHost") {Return $false}
    Else {Return $true}
}
Function Get-OWAInstall
{
    # Crude way of checking if Office Web Apps is already installed
    If (Test-Path "$env:CommonProgramFiles\Microsoft Shared\Web Server Extensions\$env:spVer\BIN\stsadm.exe")
    {
        return $true
    }
    Else {return $false}
}

# ====================================================================================
# Func: Show-Progress
# Desc: Shows a row of dots to let us know that $process is still running
# From: Brian Lalancette, 2012
# ====================================================================================
Function Show-Progress ($process, $color, $interval)
{
    $indicators = @("/","-","\","|")
    While (Get-Process -Name $process -ErrorAction SilentlyContinue)
    {
        Write-Host -ForegroundColor $color "." -NoNewline
        Start-Sleep $interval
    }
    Write-Host -ForegroundColor $color "Done."
}

# ====================================================================================
# Func: Set-UserAccountControl
# Desc: Enables or disables User Account Control (UAC), using a 1 or a 0 (respectively) passed as a parameter
# From: Brian Lalancette, 2012
# ====================================================================================
Function Set-UserAccountControl ($flag)
{
    $regUAC = (Get-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system").GetValue("EnableLUA")
    if ($flag -eq $regUAC)
    {
        Write-Host -ForegroundColor White " - User Account Control is already" $($regUAC -replace "1","enabled." -replace "0","disabled.")
    }
    else
    {
        if ($regUAC -eq 1)
        {
            New-Item -Path "HKLM:\SOFTWARE\AutoSPInstaller\" -ErrorAction SilentlyContinue | Out-Null
			$regKey = Get-Item -Path "HKLM:\SOFTWARE\AutoSPInstaller\"
            $regKey | New-ItemProperty -Name "UACWasEnabled" -PropertyType String -Value "1" -Force | Out-Null
        }
        Write-Host -ForegroundColor White " - $($flag -replace "1","Re-enabling" -replace "0","Disabling") User Account Control (effective upon restart)..."
        Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system" -Name EnableLUA -Value $flag
    }
}
#EndRegion